package com.platform.comm.util;

import com.google.common.base.Strings;
import com.google.common.collect.Maps;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.*;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;

import java.io.*;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Description: Simple Restful interface request utils
 * <p> 返回值为JSON字符串 </p>
 *
 * @author libin Created on 2019/9/3
 **/
@Component
public final class RestUtil implements ApplicationContextAware {

    private static final String PATH_FORMAT = "{%s}";
    private static final String HTTPS_SYMBOL = "https";
    private static ApplicationContext context;
    public static final Map<String, String> HTTP_HEADER_MAP = new HashMap<>();

    static {
        HTTP_HEADER_MAP.put("C-Tenancy-Id", "110000000000");
        HTTP_HEADER_MAP.put("C-Business-Id", "20181115124830231010100100010010");
        HTTP_HEADER_MAP.put("C-App-Id", "GSP_APP_001");
    }

    @SuppressWarnings("NullableProblems")
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        context = applicationContext;
    }

    /**
     * GET请求（用户密码验证）
     *
     * @param url      请求路径
     * @param username 用户名
     * @param password 密码
     * @param headers  请求头信息
     * @param params   请求参数
     * @return 响应JSON
     */
    public static String getWithBasicAuthentication(String url, String username, String password,
                                                    Map<String, String> headers, Map<String, String> params) {
        HttpHeaders httpHeaders = getHttpHeaders(headers);
        httpHeaders.setBasicAuth(username, password, StandardCharsets.US_ASCII);
        return get(url, httpHeaders, params);
    }

    /**
     * GET请求（Token）
     *
     * @param url     请求路径
     * @param token   Token
     * @param headers 请求头信息
     * @param params  请求参数
     * @return 响应JSON
     */
    public static String getWithBearerToken(String url, String token, Map<String, String> headers,
                                            Map<String, String> params) {
        HttpHeaders httpHeaders = getHttpHeaders(headers);
        httpHeaders.setBearerAuth(token);
        return get(url, httpHeaders, params);
    }

    /**
     * GET请求（不需要验证）
     *
     * @param url     请求路径
     * @param headers 请求头信息
     * @param params  请求参数
     * @return 响应JSON
     */
    public static String getWithoutAuthentication(String url, Map<String, String> headers, Map<String, String> params) {
        return get(url, getHttpHeaders(headers), params);
    }

    public static String getForSimple(String url, Map<String, String> params) {
        if (Strings.isNullOrEmpty(url)) {
            throw new RuntimeException("Request url could not empty");
        }
        RestTemplate restTemplate = context.getBean("restTemplate", RestTemplate.class);
        return restTemplate.getForObject(buildParams(url, params), String.class);
    }

    private static String get(String url, HttpHeaders headers, Map<String, String> params) {
        RestTemplate restTemplate = context.getBean("restTemplate", RestTemplate.class);
        if (url.contains(HTTPS_SYMBOL)) {
            restTemplate = context.getBean("restTemplateForHttps", RestTemplate.class);
        }
        HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(headers);

        ResponseEntity<String> responseEntity = restTemplate.exchange(buildParams(url, params), HttpMethod.GET, requestEntity, String.class);
        return responseEntity.getBody();
    }

    /**
     * 展开参数
     *
     * @param url        查询基本路径
     * @param parameters 参数
     * @return 完整URL
     */
    public static URI buildParams(String url, Map<String, String> parameters) {
        UriComponents uriComponents = UriComponentsBuilder.fromUriString(url).build();
        if (CollectionUtils.isEmpty(parameters)) {
            return uriComponents.toUri();
        }

        Map<String, String> queryVariable = Maps.newHashMap();
        // 处理路径参数
        Map<String, String> pathVariable = Maps.newHashMap();
        parameters.forEach((key, value) -> {
            if (url.contains(String.format(PATH_FORMAT, key))) {
                pathVariable.put(key, value);
            } else {
                queryVariable.put(key, value);
            }
        });
        uriComponents = uriComponents.expand(pathVariable);

        // 处理查询参数
        String queryKeys = queryVariable.entrySet().stream().map(entry -> String.format("%s=%s", entry.getKey(), entry.getValue()))
                .collect(Collectors.joining("&"));
        uriComponents = UriComponentsBuilder.newInstance()
                .scheme(uriComponents.getScheme())
                .host(uriComponents.getHost())
                .port(uriComponents.getPort())
                .path(Objects.requireNonNull(uriComponents.getPath()))
                .query(queryKeys)
                .buildAndExpand(queryVariable.values().toArray(new Object[0])).encode();

        return uriComponents.toUri();
    }

    private static HttpHeaders getHttpHeaders(Map<String, String> headers) {
        HttpHeaders httpHeaders = new HttpHeaders();
        List<MediaType> mediaTypeList = Arrays.asList(MediaType.APPLICATION_JSON_UTF8,
                MediaType.APPLICATION_FORM_URLENCODED);
        httpHeaders.setAccept(mediaTypeList);
        httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        httpHeaders.setAcceptCharset(Arrays.asList(StandardCharsets.UTF_8, StandardCharsets.ISO_8859_1));
        if (!CollectionUtils.isEmpty(headers)) {
            headers.forEach(httpHeaders::add);
        }
        return httpHeaders;
    }


    /**
     * 可以设置请求头参数和请求体参数的post方法
     *
     * @param url     请求路径
     * @param headers 请求头
     * @param params  请求参数
     * @return JSON
     */
    public static Map<String,Object> post(String url, Map<String, String> headers, Object params) {
        RestTemplate restTemplate = SpringBeansUtil.getBean(RestTemplate.class, "restTemplate");
        //header信息，包括了http basic认证信息
        MultiValueMap<String, String> headersMap = new LinkedMultiValueMap<>();
        for (String key : headers.keySet()) {
            headersMap.add(key, headers.get(key));
        }

        //merge成为一个HttpEntity
        HttpEntity<Object> multiValueMapHttpEntity = new HttpEntity<>(params, headersMap);

        //当响应的值为400或401时候也要正常响应，不要抛出异常
        restTemplate.setErrorHandler(new DefaultResponseErrorHandler() {
            @Override
            public void handleError(ClientHttpResponse response) throws IOException {
                if (response.getRawStatusCode() != 400 || response.getRawStatusCode() != 401) {
                    super.handleError(response);
                }
            }
        });

        ResponseEntity<Map> responseEntity = restTemplate.exchange(url, HttpMethod.POST, multiValueMapHttpEntity, Map.class);
        return responseEntity.getBody();
    }


    /**
     * 可以设置请求头参数和请求体参数的post方法
     *
     * @param url     请求路径
     * @param headers 请求头信息
     * @param params  请求参数
     * @return JSON
     */
    public static String postWithBearerToken(String url, String token, Map<String, String> headers, String params) {
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setContentType(MediaType.APPLICATION_JSON);
        //请求参数转换成json格式
        if (StringUtils.isNotEmpty(token)) {
            httpHeaders.add("authorization", "bearer " + token);
        }
        if (!CollectionUtils.isEmpty(headers)) {
            headers.forEach(httpHeaders::add);
        }

        RestTemplate restTemplate = SpringBeansUtil.getBean(RestTemplate.class, "restTemplate");
        HttpEntity<String> entity = new HttpEntity<>(params, httpHeaders);
        //当响应的值为400或401时候也要正常响应，不要抛出异常
        restTemplate.setErrorHandler(new DefaultResponseErrorHandler() {
            @Override
            public void handleError(ClientHttpResponse response) throws IOException {
                if (response.getRawStatusCode() != 400 || response.getRawStatusCode() != 401) {
                    super.handleError(response);
                }
            }
        });
        ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.POST, entity, String.class, new Object[]{null});
        return responseEntity.getBody();
    }

    public static String postWithBearerToken(String url, String params) {
        return postWithBearerToken(url, null, HTTP_HEADER_MAP, params);
    }


    /**
     * 上传文件到文件系统
     *
     * @param file 文件
     * @return 文件路径
     */
    public static String upload(File file, String uploadUrl) throws IOException {
        RestTemplate restTemplate = SpringBeansUtil.getBean(RestTemplate.class, "restTemplate");
        if (!file.exists()) {
            return null;
        }
        //将文件传入文件管理系统
        FileSystemResource resource = new FileSystemResource(file);
        MultiValueMap<String, Object> param = new LinkedMultiValueMap<>();
        param.add("file", resource);
        ResponseEntity<String> pathEntity = restTemplate.postForEntity(uploadUrl, param, String.class);
        //删除本地文件
        Files.deleteIfExists(file.toPath());
        //返回文件路径 例如：risk/file/customerReport/20190621/19062113582560509589.pdf
        return pathEntity.getBody();
    }

    public static InputStream downLoadFile(String downloadUrl){
        RestTemplate restTemplate = SpringBeansUtil.getBean(RestTemplate.class, "restTemplate");
        Map<String, String> headers = Maps.newHashMap();
        HttpHeaders httpHeaders = getHttpHeaders(headers);
        InputStream inputStream = null;
        ResponseEntity<byte[]> pathEntity = restTemplate.exchange(
                downloadUrl,
                HttpMethod.POST,
                new HttpEntity<byte[]>(httpHeaders),
                byte[].class);
        byte[] result = pathEntity.getBody();
        inputStream = new ByteArrayInputStream(result);
        return inputStream;
    }

    public static long copy(InputStream source, OutputStream sink) {
        long nread = 0L;
        try {
            byte[] buf = new byte[8092];
            int n;
            while ((n = source.read(buf)) > 0) {
                sink.write(buf, 0, n);
                nread += n;
            }
            source.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return nread;
    }
}
